#include "placement.h"

Placement::Placement()
{
}

bool caseInsensitiveLessThan(Transistor *t1, Transistor *t2)
{
    return t1->getPosIndex() < t2->getPosIndex();
}

void Placement::placeTransistors(Cell *cell, int alg, QPlainTextEdit *debug)
{
    this->cell = cell;
    this->debug = debug;
    cell->reset();
    cell->folding();

    resetTransistorsRoutePosition();

    switch(alg)
    {
        //dummy placement
        case 1:
            dummyPlacement();
            break;

        //pseudoBoolean Placement
        case 2:
            pbsPlacement();
            break;

        default:
            return;
    }

    qSort(cell->nTransistors->begin(), cell->nTransistors->end(), caseInsensitiveLessThan);
    qSort(cell->pTransistors->begin(), cell->pTransistors->end(), caseInsensitiveLessThan);
}

void Placement::pbsPlacement()
{
    //WRITE TEMP FILE
    QFile outFile(QDir::currentPath() + "\\tools\\temp.sp");

    if(!outFile.open(QIODevice::WriteOnly | QIODevice::Text))
    {
        debug->appendHtml("<font color='red'> Failed writing temp.sp file for PBS solver </font>");
        return;
    }
    QTextStream out(&outFile);

    cell->sortByGateNetName();

    out << cell->toString();

    outFile.close();

    //RUN PBSIG
    QProcess *pbsig = new QProcess();
    //pbsig->setStandardOutputFile("pbsig.out");
    pbsig->start("\"" + QDir::currentPath() +"\\tools\\pbsig.exe\" " +
                "\"" + QDir::currentPath() + "\\tools\\temp.sp\" " +
                "\"" + QDir::currentPath() + "\\tools\\temp.cnf\" 1");

    if(!pbsig->waitForFinished(3600000)) return;

    debug->appendPlainText(pbsig->readAll());

    //READ .PL FILE
    QFile inFile(QDir::currentPath() + "\\tools\\temp.pl");

    if(!inFile.open(QIODevice::ReadOnly | QIODevice::Text))
        return;

    QTextStream in(&inFile);
    QStringList pos = in.readLine().split(" ");

    inFile.close();

    //SET TRANSISTORS POSITIONS

    int nRows = pos.at(2).toInt();
    int nCols = pos.at(3).toInt();
    int nVars = nRows*nCols;

    int curIndex = 4;
    foreach(Transistor *t, *(cell->nTransistors))
    {
        int col = 1;
        for(int i=curIndex; i<curIndex + nVars; i++)
        {
            if(pos.at(i).toInt() > 0)
            {
                t->posIndex = col;
            }
            col++;
        }
        t->flipped = pos.at(curIndex + nVars).toInt() > 0;
        curIndex += nVars + 1;
    }

    foreach(Transistor *t, *(cell->pTransistors))
    {
        int col = 1;
        for(int i=curIndex; i<curIndex + nVars; i++)
        {
            if(i == pos.size())
                return;
            if(pos.at(i).toInt() > 0)
            {
                t->posIndex = col;
            }
            col++;
        }
        t->flipped = pos.at(curIndex + nVars).toInt() > 0;
        curIndex += nVars + 1;
    }
}

boolean Placement::hasGap(Transistor* t,  QList<Transistor*> *transistors )
{
    int i = transistors->indexOf(t);

    if(i > 0)
    {
        QString previousTranRightNet = transistors->at(i-1)->drainNetName;
        if(transistors->at(i-1)->flipped)
            previousTranRightNet = transistors->at(i-1)->sourceNetName;

        QString nextTranLeftNet = transistors->at(i)->sourceNetName;
        if(transistors->at(i)->flipped)
            nextTranLeftNet = transistors->at(i)->drainNetName;

        return previousTranRightNet.compare(nextTranLeftNet) != 0;
    }

    return false;
}

void Placement::dummyPlacement()
{
    int curIndex = 1;

    foreach(Transistor* t, *(cell->nTransistors))
    {
        if(hasGap(t, cell->nTransistors))
        {
            t->setPosIndex(++curIndex);
            curIndex++;
        }
        else
            t->setPosIndex(curIndex++);
    }

    curIndex = 1;
    foreach(Transistor* t, *(cell->pTransistors))
    {

        if(hasGap(t, cell->pTransistors))
        {
            t->setPosIndex(++curIndex);
            curIndex++;
        }
        else
            t->setPosIndex(curIndex++);

    }
}

void Placement::resetTransistorsRoutePosition()
{
    foreach(Transistor *t, *(cell->pTransistors))
        t->routedPos = QPointF(0,0);

    foreach(Transistor *t, *(cell->nTransistors))
        t->routedPos = QPointF(0,0);
}
